// 主要是为了解决在`isInCompositionMode`模式下，进行`inlineStyle`切换的时候，会出现
// 刚刚触发的`inlineStyle`变化并不会作用到接下来的输入的问题。目前的处理方式就是在
// toggle inline style以及换行的时候，默认添加一个`\u200B`字符
import { Modifier, EditorState, SelectionState } from "draft-js";
import { splitAtLastCharacterAndForwardSelection } from "../utils/editorState";

function StyleControlPlugin() {
  const selectionWithNonWidthCharacter = {};

  this.apply = getState => {
    const { hooks } = getState();

    // 1. 首先判断是在`isCollapsed`模式下
    // 2. 查看触发`inlineStyle`变化位置的前一个字符是否是`\u200B`，如果是的话，就将新的
    //    inlineStyle应用到它上面，否则就插入一个`\u200B`字符
    hooks.afterInlineStyleApplied.tap(
      "StyleControlPlugin",
      (newEditorState, editorState, inlineStyle) => {
        const nextEditorState = newEditorState || editorState;
        const currentContent = nextEditorState.getCurrentContent();
        const selection = nextEditorState.getSelection();
        const currentInlineStyle = nextEditorState.getCurrentInlineStyle();
        if (selection.isCollapsed()) {
          const startKey = selection.getStartKey();
          const anchorOffset = selection.getAnchorOffset();
          const block = currentContent.getBlockForKey(startKey);

          // 查看一下最后的字符是否是`\u200B`;如果是的话，则直接将它替换掉，而不是再添加一个
          const len = block.getLength();
          const text = block.getText();
          const lastCharacter = text[len - 1];
          let newCurrentState;
          let action;
          let markerSelection;

          if (lastCharacter === "\u200B") {
            markerSelection = selection.merge({
              anchorOffset: len - 1,
              focusOffset: len
            });
            newCurrentState = Modifier.applyInlineStyle(
              currentContent,
              markerSelection,
              inlineStyle
            );
            action = "change-inline-style";
          } else {
            markerSelection = selection;
            newCurrentState = Modifier.insertText(
              currentContent,
              selection,
              "\u200B",
              currentInlineStyle,
              null
            );
            action = "insert-characters";

            if (!selectionWithNonWidthCharacter[startKey])
              selectionWithNonWidthCharacter[startKey] = {};
            const group = selectionWithNonWidthCharacter[startKey];
            group[anchorOffset] = selection;
          }

          const next = EditorState.push(
            nextEditorState,
            newCurrentState,
            action
          );
          // 如果不设置forceSelection的话，当触发`inlineStyle`改变的时候，光标并不会被
          // 放置到新插入的`\u200B`的后面；如果不进行`forceSelection`的话，只能够是通过光标移动来解决了
          return EditorState.forceSelection(
            next,
            newCurrentState.getSelectionAfter()
          );
        }
        return null;
      }
    );

    // 存在的问题，新开一个editor，然后只是敲入几行空格，最后再`backspace`删除直到第一行的时候
    // 切换成中文输入法然后输入东西会报错；
    // 1. 当进行换行的时候，需要判断最后一个字符是否是`\u200B`；如果是的话，在该字符的前面进行中断；
    // 2. 如果不是，就要看最后一个字符是否有inlineStyle，如果有就插入一个新的`\u200B`；没有就不做
    // 处理
    hooks.handleKeyCommand.tap("StyleControlPlugin", (command, editorState) => {
      const selection = editorState.getSelection();
      const currentContent = editorState.getCurrentContent();
      const endKey = selection.getEndKey();
      const block = currentContent.getBlockForKey(endKey);
      const blockSize = block.getLength();
      const text = block.getText();

      if (!selection.isCollapsed()) return "not-handled";

      if (command === "split-block") {
        if (text) {
          const charAtLast = block.getText()[blockSize - 1];
          if (charAtLast === "\u200B") {
            const newState = splitAtLastCharacterAndForwardSelection(
              editorState
            );

            delete selectionWithNonWidthCharacter[endKey][blockSize - 1];
            const afterBlock = newState
              .getCurrentContent()
              .getBlockAfter(endKey);
            const afterSelection = newState.getSelection();
            const afterBlockKey = afterBlock.getKey();
            if (!selectionWithNonWidthCharacter[afterBlockKey]) {
              selectionWithNonWidthCharacter[afterBlockKey] = {};
            }
            const group = selectionWithNonWidthCharacter[afterBlockKey];
            group[0] = afterSelection.merge({
              anchorOffset: 0,
              focusOffset: 0
            });

            hooks.setState.call(newState);
            return "handled";
          }
        }

        const currentStyle = block.getInlineStyleAt(blockSize - 1);
        if (currentStyle.size) {
          // Modifier.insertText其实是有`selection`的变化的
          const nextContent = Modifier.insertText(
            currentContent,
            selection,
            "\u200B",
            currentStyle,
            null
          );
          const newEditorState = EditorState.push(editorState, nextContent);
          const newState = splitAtLastCharacterAndForwardSelection(
            newEditorState
          );

          // 删除因为split造成的行头存在的`\u200B`字符
          const afterBlock = newState.getCurrentContent().getBlockAfter(endKey);
          const afterSelection = newState.getSelection();
          const afterBlockKey = afterBlock.getKey();
          if (!selectionWithNonWidthCharacter[afterBlockKey]) {
            selectionWithNonWidthCharacter[afterBlockKey] = {};
          }
          const group = selectionWithNonWidthCharacter[afterBlockKey];
          group[0] = afterSelection.merge({
            anchorOffset: 0,
            focusOffset: 0
          });

          hooks.setState.call(newState);
          return "handled";
        }
      }
    });

    hooks.didUpdate.tap("RemoveLastNonWidthCharacterPlugin", editorState => {
      // return
      const selection = editorState.getSelection();
      const currentState = editorState.getCurrentContent();
      const isInCompositionMode = editorState.isInCompositionMode();

      if (selection.isCollapsed() && !isInCompositionMode) {
        const currentSelectionPosition = selection.getAnchorOffset();
        const currentStartKey = selection.getStartKey();
        const group = selectionWithNonWidthCharacter[currentStartKey];
        if (group) {
          const keys = Object.keys(group);

          const newState = keys.reduce((es, key) => {
            const markerSelection = group[key];
            const content = es.getCurrentContent();
            const block = content.getBlockForKey(markerSelection.getStartKey());
            const blockText = block.getText();
            const markerSelectionPosition = markerSelection.getAnchorOffset();
            if (
              Math.abs(markerSelectionPosition - currentSelectionPosition) <= 1
            )
              return es;

            delete group[key];

            // TODO：这里是一个比较粗糙的判断，如果说当前的位置还是一个空字符的话，那就处理，否则
            // 就不处理；其实本身通过`selectionWithNonWidthCharacter`来进行记录其实都不是一个
            // 合理的方案，会出现很多的不一致的情况，比如说在行首，触发了inlineStyle这个时候马上删除
            // 你接下来输入的字符第一个会被删除掉。。。因为 selectionWithNonWidthCharacter还标记
            // 着行首是一个空字符。。。
            if (blockText[markerSelectionPosition] !== "\u200B") return;

            const newContent = Modifier.removeRange(
              content,
              markerSelection.merge({
                focusOffset: markerSelectionPosition + 1
              }),
              "backward"
            );

            // 通过设置新的`selectionAfter`为了解决，当比如中文输入完以后，光标应该回到哪个位置；
            // 如果没有这个的设置的话，光标会被放置到刚刚输入中文开始的位置。
            // const newState = EditorState.push(editorState, newContent, 'delete-character')
            const newState = EditorState.push(
              editorState,
              newContent.set(
                "selectionAfter",
                new SelectionState({
                  anchorKey: currentStartKey,
                  anchorOffset: selection.getAnchorOffset() - 1,
                  focusKey: currentStartKey,
                  focusOffset: selection.getAnchorOffset() - 1,
                  isBackward: false,
                  hasFocus: true
                })
              ),
              "delete-character"
            );

            return newState;
            // const newState = EditorState.push(editorState, newContent.set('selectionAfter', selection), 'delete-character')
            // return EditorState.forceSelection(newState, es.getSelection())
          }, editorState);

          if (newState && newState !== editorState)
            hooks.setState.call(newState);
        }
      }
    });
  };
}

export default StyleControlPlugin;
